/*
* Copyright (C) 2020 ~ 2024 chaigec All Rights Reserved.
*
* Author:     chaigec <chaigec@163.com>
* Maintainer: chaigec <chaigec@163.com>

 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
*/

#include "MkAmf.h"
#include "MkUtil/MkLog.h"

#define MkCheckLen(pBegin, pEnd, nLen)     if (pEnd<pBegin || nLen > static_cast<Uint32>(pEnd-pBegin)) { return nullptr; }

const Uint8* CMkAmf::DecodeUint8(const Uint8* pBegin, const Uint8* pEnd, Uint8& Value)
{
    MkCheckLen(pBegin, pEnd, sizeof(Value));
    Value = *pBegin;
    return pBegin + sizeof(Value);
}

const Uint8* CMkAmf::DecodeUint16(const Uint8* pBegin, const Uint8* pEnd, Uint16& Value)
{
    MkCheckLen(pBegin, pEnd, sizeof(Value));
    Value = (pBegin[0] << 8) | (pBegin[1]);
    return pBegin + sizeof(Value);
}

const Uint8* CMkAmf::DecodeUint24(const Uint8* pBegin, const Uint8* pEnd, Uint32& Value)
{
    MkCheckLen(pBegin, pEnd, sizeof(Value) - 1);
    Value = (pBegin[0] << 16) | (pBegin[1] << 8) | (pBegin[2]);
    return pBegin + sizeof(Value) - 1;
}

const Uint8* CMkAmf::DecodeUint32(const Uint8* pBegin, const Uint8* pEnd, Uint32& Value)
{
    MkCheckLen(pBegin, pEnd, sizeof(Value));
    Value = (pBegin[0] << 24) | (pBegin[1] << 16) | (pBegin[2] << 8) | (pBegin[3]);
    return pBegin + sizeof(Value);
}

const Uint8* CMkAmf::DecodeNumber(const Uint8* pBegin, const Uint8* pEnd, double& dValue)
{
    MkCheckLen(pBegin, pEnd, sizeof(dValue));
    Uint8 *pCo = (Uint8*)&dValue;
    pCo[0] = pBegin[7];
    pCo[1] = pBegin[6];
    pCo[2] = pBegin[5];
    pCo[3] = pBegin[4];
    pCo[4] = pBegin[3];
    pCo[5] = pBegin[2];
    pCo[6] = pBegin[1];
    pCo[7] = pBegin[0];
    return pBegin + sizeof(dValue);
}

const Uint8* CMkAmf::DecodeString(const Uint8* pBegin, const Uint8* pEnd, MkString& Value)
{
    Uint16 nLen = 0;
    pBegin = DecodeUint16(pBegin, pEnd, nLen);
    MkCheckPoint(pBegin, "decode string len failed\n");

    MkCheckLen(pBegin, pEnd, nLen);
    Value = MkString((Int8*)pBegin, nLen);
    return pBegin + nLen;
}

const Uint8* CMkAmf::DecodeLongString(const Uint8* pBegin, const Uint8* pEnd, MkString& Value)
{
    Uint32 nLen = 0;
    pBegin = DecodeUint32(pBegin, pEnd, nLen);
    MkCheckPoint(pBegin, "decode string len failed\n");

    MkCheckLen(pBegin, pEnd, nLen);
    Value = MkString((Int8*)pBegin, nLen);
    return pBegin + nLen;
}

const Uint8* CMkAmf::DecodeBoolean(const Uint8* pBegin, const Uint8* pEnd, BOOL& bValue)
{
    MkCheckLen(pBegin, pEnd, 1);
    bValue = (0 != pBegin[0]) ? TRUE : FALSE;
    return pBegin + 1;
}

const Uint8* CMkAmf::DecodeDate(const Uint8* pBegin, const Uint8* pEnd, CMkTime& DTime)
{
    double dValue;
    Uint16 Utc;
    pBegin = DecodeNumber(pBegin, pEnd, dValue);
    MkCheckPoint(pBegin, "decode date number failed\n");

    pBegin = DecodeUint16(pBegin, pEnd, Utc);
    MkCheckPoint(pBegin, "decode date utc time failed\n");
    DTime = CMkTime(Utc, (Uint64)dValue * 1000 * 1000 * 1000);
    return pBegin;
}

const Uint8* CMkAmf::DecodeAmf3Integer(const Uint8* pBegin, const Uint8* pEnd, Uint32& Value)
{
    Uint32 Index = 0;
    while (Index <= 2) {
        if (pBegin[Index] & 0x80) {
            Value <<= 7;
            Value |= (pBegin[Index] & 0x7f);
            Index++;
        } else {
            break;
        }
    }
    if (Index > 2) {
        Value <<= 8;
        Value |= pBegin[3];
        if (Value > MkAmf3IntegerMax) {
            Value -= (1 << 29);
        }
    } else {
        Value <<= 7;
        Value |= pBegin[Index];
    }
    return pBegin + (Index > 2 ? 4 : (Index + 1));
}

const Uint8* CMkAmf::DecodeAmf3String(const Uint8* pBegin, const Uint8* pEnd, MkString& Value)
{
    Uint32 nLen = 0;
    pBegin = DecodeAmf3Integer(pBegin, pEnd, nLen);
    MkCheckPoint(pBegin, "decode amf3 integer failed\n");
    if (0 != (nLen & 0x01)) {
        nLen = nLen >> 1;
        MkCheckLen(pBegin, pEnd, nLen);
        Value = MkString((Int8*)pBegin, nLen);
        pBegin += nLen;
    }
    return pBegin;
}

Uint8* CMkAmf::EncodeUint8(Uint8* pBegin, Uint8* pEnd, Uint8 Value)
{
    MkCheckLen(pBegin, pEnd, sizeof(Value));
    pBegin[0] = Value;
    return pBegin + sizeof(Value);
}

Uint8* CMkAmf::EncodeUint16(Uint8* pBegin, Uint8* pEnd, Uint16 Value)
{
    MkCheckLen(pBegin, pEnd, sizeof(Value));
    pBegin[0] = Value >> 8;
    pBegin[1] = Value & 0xFF;
    return pBegin + sizeof(Value);
}

Uint8* CMkAmf::EncodeUint24(Uint8* pBegin, Uint8* pEnd, Uint32 Value)
{
    MkCheckLen(pBegin, pEnd, sizeof(Value) - 1);
    pBegin[0] = Value >> 16;
    pBegin[1] = Value >> 8;
    pBegin[2] = Value & 0xFF;
    return pBegin + sizeof(Value) - 1;
}

Uint8* CMkAmf::EncodeUint32(Uint8* pBegin, Uint8* pEnd, Uint32 Value)
{
    MkCheckLen(pBegin, pEnd, sizeof(Value));
    pBegin[0] = Value >> 24;
    pBegin[1] = Value >> 16;
    pBegin[2] = Value >> 8;
    pBegin[3] = Value & 0xFF;
    return pBegin + sizeof(Value);
}

Uint8* CMkAmf::EncodeString(Uint8* pBegin, Uint8* pEnd, const MkString& Value)
{
    Uint32 nLen = Value.size();
    if (Value.size() < 0xFFFF) {
        pBegin = EncodeUint8(pBegin, pEnd, MkAmfString);
        MkCheckPoint(pBegin, "encode string type failed\n");
        pBegin = EncodeUint16(pBegin, pEnd, static_cast<Uint16>(nLen));
        MkCheckPoint(pBegin, "encode string len failed\n");
    } else {
        pBegin = EncodeUint8(pBegin, pEnd, MkAmfLongString);
        MkCheckPoint(pBegin, "encode string type failed\n");
        pBegin = EncodeUint32(pBegin, pEnd, nLen);
        MkCheckPoint(pBegin, "encode string len failed\n");
    }
    MkCheckLen(pBegin, pEnd, nLen);
    memcpy(pBegin, Value.c_str(), nLen);
    return pBegin + nLen;
}

Uint8* CMkAmf::EncodeLongString(Uint8* pBegin, Uint8* pEnd, const MkString& Value)
{

    Uint32 nLen = Value.size();
    pBegin = EncodeUint8(pBegin, pEnd, MkAmfLongString);
    MkCheckPoint(pBegin, "encode string type failed\n");
    pBegin = EncodeUint32(pBegin, pEnd, nLen);
    MkCheckPoint(pBegin, "encode string len failed\n");
    MkCheckLen(pBegin, pEnd, nLen);
    memcpy(pBegin, Value.c_str(), nLen);
    return pBegin + nLen;
}

Uint8* CMkAmf::EncodeNumber(Uint8* pBegin, Uint8* pEnd, double Value)
{
    pBegin = EncodeUint8(pBegin, pEnd, MkAmfNumber);
    MkCheckPoint(pBegin, "encode amf number type failed\n");
    MkCheckLen(pBegin, pEnd, sizeof(Value));

    Uint8* pValue = (Uint8*)&Value;
    pBegin[0] = pValue[7];
    pBegin[1] = pValue[6];
    pBegin[2] = pValue[5];
    pBegin[3] = pValue[4];
    pBegin[4] = pValue[3];
    pBegin[5] = pValue[2];
    pBegin[6] = pValue[1];
    pBegin[7] = pValue[0];
    return pBegin + sizeof(Value);
}

Uint8* CMkAmf::EncodeBeginObject(Uint8* pBegin, Uint8* pEnd)
{
    return EncodeUint8(pBegin, pEnd, MkAmfTypeObject);
}

Uint8* CMkAmf::EncodeBool(Uint8* pBegin, Uint8* pEnd, BOOL bValue)
{
    pBegin = EncodeUint8(pBegin, pEnd, MkAmfBoolean);
    MkCheckPoint(pBegin, "encode amf boolean type failed\n");
    MkCheckLen(pBegin, pEnd, 1);
    pBegin[0] = bValue ? 0x01 : 0x00;
    return pBegin + 1;
}

Uint8* CMkAmf::EncodeNamedString(Uint8* pBegin, Uint8* pEnd, const MkString& Key, const MkString& Value)
{
    pBegin = EncodeName(pBegin, pEnd, Key);
    MkCheckPoint(pBegin, "encode name failed\n");
    return EncodeString(pBegin, pEnd, Value);
}

Uint8* CMkAmf::EncodeEndObject(Uint8* pBegin, Uint8* pEnd)
{
    return EncodeUint24(pBegin, pEnd, MkAmfObjectEnd);
}

Uint8* CMkAmf::EncodeNamedNumber(Uint8* pBegin, Uint8* pEnd, const MkString& Key, double Value)
{
    pBegin = EncodeName(pBegin, pEnd, Key);
    MkCheckPoint(pBegin, "encode name failed\n");
    return EncodeNumber(pBegin, pEnd, Value);
}

Uint8* CMkAmf::EncodeNamedBool(Uint8* pBegin, Uint8* pEnd, const MkString& Key, BOOL bValue)
{
    pBegin = EncodeName(pBegin, pEnd, Key);
    MkCheckPoint(pBegin, "encode name failed\n");
    return EncodeBool(pBegin, pEnd, bValue);
}

Uint8* CMkAmf::EncodeDate(Uint8* pBegin, Uint8* pEnd, const CMkTime& DTime)
{
    double dValue = double(DTime.GetUSecond()) / (1000 * 1000 * 1000);
    pBegin = EncodeNumber(pBegin, pEnd, dValue);
    MkCheckPoint(pBegin, "encode date number failed\n");
    return EncodeUint16(pBegin, pEnd, static_cast<Uint16>(DTime.GetTvSec()));
}

Uint8* CMkAmf::EncodeObject(Uint8* pBegin, Uint8* pEnd, const MkVector<MkAmfObject>& VecObj)
{
    //pBegin = EncodeBeginObject(pBegin, pEnd);
    //MkCheckPoint(pBegin, "encode object begin failed\n");
    MkVector<MkAmfObject>::const_iterator it = VecObj.begin();
    for (; it != VecObj.end(); it++) {
        pBegin = EncodeOneObject(pBegin, pEnd, *it);
        MkCheckPoint(pBegin, "encode failed\n");
    }
    // return EncodeEndObject(pBegin, pEnd);
    return pBegin;
}

Uint8* CMkAmf::EncodeEcmaArray(Uint8* pBegin, Uint8* pEnd, const MkVector<MkAmfObject>& VecObj)
{
    pBegin = EncodeUint8(pBegin, pEnd, MkAmfEcmaArray);
    MkCheckPoint(pBegin, "encode type failed\n");
    pBegin = EncodeUint32(pBegin, pEnd, VecObj.size());
    MkCheckPoint(pBegin, "encod object size failed\n");
    MkVector<MkAmfObject>::const_iterator it = VecObj.begin();
    for (; it != VecObj.end(); it++) {
        pBegin = EncodeOneObject(pBegin, pEnd, *it);
        MkCheckPoint(pBegin, "encode one object failed\n");
    }
    return EncodeEndObject(pBegin, pEnd);
}

Uint8* CMkAmf::EncodeArray(Uint8* pBegin, Uint8* pEnd, const MkVector<MkAmfObject>& VecObj)
{
    pBegin = EncodeUint8(pBegin, pEnd, MkAmfStrictArray);
    MkCheckPoint(pBegin, "encode type failed\n");
    pBegin = EncodeUint32(pBegin, pEnd, VecObj.size());
    MkCheckPoint(pBegin, "encod object size failed\n");
    MkVector<MkAmfObject>::const_iterator it = VecObj.begin();
    for (; it != VecObj.end(); it++) {
        pBegin = EncodeOneObject(pBegin, pEnd, *it);
        MkCheckPoint(pBegin, "encode one object failed\n");
    }
    return pBegin;
}

const Uint8* CMkAmf::DecodeObject(const Uint8* pBegin, const Uint8* pEnd, MkVector<MkAmfObject>& VecObj, BOOL bDecodeName)
{
    while (nullptr != pBegin && pBegin < pEnd) {
        if (pEnd - pBegin >= 3) {
            Uint32 Value;
            pBegin = DecodeUint24(pBegin, pEnd, Value);
            //get object end
            if (MkAmfObjectEnd == Value) {
                break;
            }
            pBegin -= 3;
        }
        MkAmfObject Obj;
        pBegin = DecodeOneObject(pBegin, pEnd, bDecodeName, Obj);
        if (nullptr != pBegin) {
            VecObj.push_back(Obj);
        }
    }
    if (pBegin > pEnd) {
        return nullptr;
    }
    return pBegin;
}

const Uint8* CMkAmf::DecodeEcmaArray(const Uint8* pBegin, const Uint8* pEnd, MkVector<MkAmfObject>& VecObj, BOOL bDecodeName)
{
    Uint32 VecSize = 0;
    pBegin = DecodeUint32(pBegin, pEnd, VecSize);
    MkCheckPoint(pBegin, "decode vecSize failed\n");
    return DecodeObject(pBegin, pEnd, VecObj, bDecodeName);
}

const Uint8* CMkAmf::DecodeArray(const Uint8* pBegin, const Uint8* pEnd, MkVector<MkAmfObject>& VecObj, BOOL bDecodeName)
{
    Uint32 VecSize = 0;
    pBegin = DecodeUint32(pBegin, pEnd, VecSize);
    MkCheckPoint(pBegin, "decode vecSize failed\n");
    for (Uint32 i = 0; i < VecSize; i++) {
        MkAmfObject Obj;
        pBegin = DecodeOneObject(pBegin, pEnd, bDecodeName, Obj);
        MkCheckPoint(pBegin, "encode one object name failed\n");
        VecObj.push_back(Obj);
    }
    return pBegin;
}

const Uint8* CMkAmf::DecodeAmf3Object(const Uint8* pBegin, const Uint8* pEnd, MkVector<MkAmfObject>& VecObj, BOOL bDecodeName)
{
    return pBegin;
}

Uint8* CMkAmf::EncodeName(Uint8* pBegin, Uint8* pEnd, const MkString& Name)
{
    Uint16 nLen = static_cast<Uint16>(Name.size());
    pBegin = EncodeUint16(pBegin, pEnd, nLen);
    MkCheckPoint(pBegin, "encode name string key failed\n");

    MkCheckLen(pBegin, pEnd, nLen);
    memcpy(pBegin, Name.c_str(), nLen);
    return pBegin + nLen;
}

Uint8* CMkAmf::EncodeOneObject(Uint8* pBegin, Uint8* pEnd, const MkAmfObject& Obj)
{
    if (!Obj.Name.empty()) {
        pBegin = EncodeName(pBegin, pEnd, Obj.Name);
    }

    switch (Obj.DataType) {
    case MkAmfBoolean:
        pBegin = EncodeBool(pBegin, pEnd, Obj.bValue);
        break;
    case MkAmfTypeObject:
        pBegin = EncodeBeginObject(pBegin, pEnd);
        pBegin = EncodeObject(pBegin, pEnd, Obj.VecObjectValue);
        pBegin = EncodeEndObject(pBegin, pEnd);
        break;
    case MkAmfString:
    case MkAmfLongString:
        pBegin = EncodeString(pBegin, pEnd, Obj.strValue);
        break;
    case MkAmfXmlDoc:
        pBegin = EncodeLongString(pBegin, pEnd, Obj.strValue);
        break;
    case MkAmfNumber:
        pBegin = EncodeNumber(pBegin, pEnd, Obj.NumberValue);
        break;
    case MkAmfNull:
        pBegin = EncodeUint8(pBegin, pEnd, MkAmfNull);
        break;
    case MkAmfEcmaArray:
        pBegin = EncodeEcmaArray(pBegin, pEnd, Obj.VecObjectValue);
        break;
    case MkAmfStrictArray:
        pBegin = EncodeArray(pBegin, pEnd, Obj.VecObjectValue);
        break;
    case MkAmfDate:
        pBegin = EncodeDate(pBegin, pEnd, Obj.TimeValue);
        break;
    default:
        pBegin = nullptr;
        break;
    }
    return pBegin;
}

const Uint8* CMkAmf::DecodeOneObject(const Uint8* pBegin, const Uint8* pEnd, BOOL bDecodeName, MkAmfObject& Obj)
{
    if (bDecodeName) {
        Uint16 NameSize;
        pBegin = DecodeUint16(pBegin, pEnd, NameSize);
        MkCheckPoint(pBegin, "get name size failed\n");
        MkCheckLen(pBegin, pEnd, NameSize);
        Obj.Name = MkString((Int8*)pBegin, NameSize);
        pBegin += NameSize;
    }
    Uint8 AmfType = 0;
    pBegin = DecodeUint8(pBegin, pEnd, AmfType);
    MkCheckPoint(pBegin, "get amf type failed\n");
    Obj.DataType = (MkAmfType)AmfType;
    switch (AmfType) {
    case MkAmfNumber:
        pBegin = DecodeNumber(pBegin, pEnd, Obj.NumberValue);
        break;
    case MkAmfBoolean:
        pBegin = DecodeBoolean(pBegin, pEnd, Obj.bValue);
        break;
    case MkAmfTypeObject:
        pBegin = DecodeObject(pBegin, pEnd, Obj.VecObjectValue, TRUE);
        break;
    case MkAmfString:
        pBegin = DecodeString(pBegin, pEnd, Obj.strValue);
        break;
    case MkAmfLongString:
    case MkAmfXmlDoc:
        pBegin = DecodeLongString(pBegin, pEnd, Obj.strValue);
        break;
    case MkAmfMovieClip:
    case MkAmfReference: //not supported 
    case MkAmfRecordset:
    case MkAmfTypedObject:
        pBegin = nullptr;
        break;
    case MkAmfNull:
    case MkAmfUndefined:
    case MkAmfUnsuppoted:
        Obj.DataType = MkAmfNull;
        break;
    case MkAmfEcmaArray:
        pBegin = DecodeEcmaArray(pBegin, pEnd, Obj.VecObjectValue, TRUE);
        break;
    case MkAmfStrictArray:
        pBegin = DecodeArray(pBegin, pEnd, Obj.VecObjectValue, FALSE);
        break;
    case MkAmfDate:
        pBegin = DecodeDate(pBegin, pEnd, Obj.TimeValue);
        break;
    case MkAmfAvmplus:
        pBegin = DecodeAmf3Object(pBegin, pEnd, Obj.VecObjectValue, TRUE);
        break;
    default:
        pBegin = nullptr;
        break;
    }
    return pBegin;
}

const Uint8* CMkAmf::Amf3DecodeOneObject(const Uint8* pBegin, const Uint8* pEnd, BOOL bDecodeName, MkAmfObject& Obj)
{
    if (bDecodeName) {
        pBegin = DecodeAmf3String(pBegin, pEnd, Obj.Name);
        MkCheckPoint(pBegin, "decode amf3 object name failed\n");
    }
    Uint8 ObjType = 0;
    Uint32 IntegerValue = 0;
    pBegin = DecodeUint8(pBegin, pEnd, ObjType);
    MkCheckPoint(pBegin, "decode object type failed\n");
    switch (ObjType) {
    case MkAmf3Null:
    case MkAmf3Undefined:
        Obj.DataType = MkAmfNull;
        break;
    case MkAmf3False:
        Obj.DataType = MkAmfBoolean;
        Obj.UintValue = FALSE;
        break;
    case MkAmf3True:
        Obj.DataType = MkAmfBoolean;
        Obj.UintValue = TRUE;
        break;
    case MkAmf3Integer:
        Obj.DataType = MkAmfNumber;
        pBegin = DecodeAmf3Integer(pBegin, pEnd, IntegerValue);
        Obj.NumberValue = (double)IntegerValue;
        break;
    case MkAmf3Double:
        Obj.DataType = MkAmfNumber;
        pBegin = DecodeNumber(pBegin, pEnd, Obj.NumberValue);
        break;
    case MkAmf3Date:
        Obj.DataType = MkAmfDate;
        pBegin = DecodeAmf3Integer(pBegin, pEnd, IntegerValue);
        if (0 != (IntegerValue & 0x01)) {
            pBegin = DecodeDate(pBegin, pEnd, Obj.TimeValue);
        } else {
            pBegin = nullptr;
        }
        break;
    case MkAmf3Object:
        pBegin = DecodeAmf3Object(pBegin, pEnd, Obj.VecObjectValue, TRUE);
        Obj.DataType = MkAmfTypeObject;
        break;
    case MkAmf3String:
    case MkAmf3XmlDoc:
    case MkAmf3Xml:
        Obj.DataType = MkAmfString;
        pBegin = DecodeAmf3String(pBegin, pEnd, Obj.strValue);
        break;
    case MkAmf3Array:
    case MkAmf3ByteArray:
    default:
        pBegin = nullptr;
        break;
    }
    return pBegin;
}